package com.gamejelly.broker.common.handler;

import com.alibaba.fastjson2.JSON;
import com.gamejelly.broker.common.Handler;
import com.gamejelly.broker.message.CmdInfo;
import com.gamejelly.broker.message.RpcResponse;
import io.netty.util.internal.StringUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultHandler implements ApplicationContextAware, Handler {

    Map<String, BrokerInvoker> invokerMap;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        invokerMap = new ConcurrentHashMap<>();
        Collection<Object> beans = applicationContext.getBeansWithAnnotation(BrokerRoute.class).values();
        for (Object bean : beans) {
            BrokerRoute route = bean.getClass().getAnnotation(BrokerRoute.class);
            if (route == null)
                continue;
            String mainRoute = StringUtil.isNullOrEmpty(route.value()) ? bean.getClass().getSimpleName() : route.value();
            for (Method method : bean.getClass().getDeclaredMethods()) {
                BrokerRoute methodAnnotation = method.getAnnotation(BrokerRoute.class);
                if (methodAnnotation == null)
                    continue;
                Class<?>[] types = method.getParameterTypes();
                Class<?> returnType = method.getReturnType();
                if (types.length == 1 && RpcResponse.class.isAssignableFrom(types[0]) && returnType.equals(Void.class)) {
                    String subRoute = StringUtil.isNullOrEmpty(methodAnnotation.value()) ? method.getName() : methodAnnotation.value();
                    invokerMap.put(mainRoute + subRoute, new BrokerInvoker(true, bean, method.getName(), method));
                } else if (types.length <= 1 && !returnType.equals(Void.class)) {
                    String subRoute = StringUtil.isNullOrEmpty(methodAnnotation.value()) ? method.getName() : methodAnnotation.value();
                    invokerMap.put(mainRoute + subRoute, new BrokerInvoker(false, bean, method.getName(), method).setParam(types.length > 0 ? types[0] : null));
                } else {

                }

            }
        }
    }

    @Override
    public void handle(RpcResponse response) throws InvocationTargetException, IllegalAccessException {
        CmdInfo cmdInfo = response.getCmdInfo();
        BrokerInvoker invoker = invokerMap.get(cmdInfo.getMainRoute() + cmdInfo.getSubRoute());
        if (invoker == null)
            return;
        if (invoker.isAsync()) {
            invoker.getMethod().invoke(invoker.getBean(), response);
        } else {
            Object result;
            if (invoker.getParam() == null) {
                result = invoker.getMethod().invoke(invoker.getBean());
            } else {
                Object param = JSON.parseObject(response.getData(), invoker.getParam());
                result = invoker.getMethod().invoke(invoker.getBean(), param);
            }
            response.reply(result);
        }

    }
}
